12. Plug-ins

Grails provides a number of extension points that allow you to extend anything from the command line interface to the runtime configuration engine. The following sections detail how to go about it.

12.1 Creating and Installing Plug-ins

Creating Plug-ins

Creating a Grails plugin is a simple matter of running the command:

grails create-plugin [PLUGIN NAME]

This will create a plugin project for the name you specify. Say for example you run grails create-plugin example. This would create a new plugin project called example.

The structure of a Grails plugin is exactly the same as a regular Grails project's directory structure, except that in the root of the plugin directory you will find a plugin Groovy file called the "plug-in descriptor".

Being a regular Grails project has a number of benefits in that you can immediately get going testing your plug-in by running:

grails run-app

When you create a plug-in by default it has not URL mappings hence controllers won't work immediately. If you're adding controllers in your plug-in create a grails-app/conf/MyUrlMappings.groovy class and add the default mapping "/$controller/$action?/$id?"() to it first.

The plug-in descriptor itself ends with the convention GrailsPlugin and is found in the root of the plug-in project. For example:

class ExampleGrailsPlugin {
   def version = 0.1

… }

All plugins must have this class in the root of their directory structure to be valid. The plugin class defines the version of the plugin and optionally various hooks into plugin extension points (covered shortly).

You can also provide additional information about your plugin using several special properties:

Here is an example from Quartz Grails plugin

class QuartzGrailsPlugin {
    def version = "0.1"
    def author = "Sergey Nebolsin"
    def authorEmail = "[email protected]"
    def title = "This plugin adds Quartz job scheduling features to Grails application."
    def description = '''
Quartz plugin allows your Grails application to schedule jobs to be
executed using a specified interval or cron expression. The underlying
system uses the Quartz Enterprise Job Scheduler configured via Spring,
but is made simpler by the coding by convention paradigm.
'''
    def documentation = "http://grails.org/Quartz+plugin"

… }

Installing & Distributing Plugins

To distribute a plugin you need to navigate to its root directory in a terminal window and then type:

grails package-plugin

This will create a zip file of the plugin starting with grails- then the plugin name and version. For example with the example plug-in created earlier this would be grails-example-0.1.zip. The package-plugin command will also generate plugin.xml file which contains machine-readable information about plugin's name, version, author, and so on.

Once you have a plugin distribution file you can navigate to a Grails project and type:

grails install-plugin /path/to/plugin/grails-example-0.1.zip

If the plugin is hosted on a remote HTTP server you can also do:

grails install-plugin http://myserver.com/plugins/grails-example-0.1.zip

Notes on excluded Artefacts

Although the create-plugin command creates certain files for you so that the plug-in can be run as a Grails application, not all of these files are included when packaging a plug-in. The following is a list of artefacts created, but not included by package-plugin:

If you need artefacts within WEB-INF it is recommended you use the _Install.groovy script (covered later), which is executed when a plug-in is installed, to provide such artefacts. In addition, although UrlMappings.groovy is excluded you are allowed to include a UrlMappings definition with a different name, such as FooUrlMappings.groovy

Distributing Plugins in Grails Plugins Repository

The preferred way of plugin distributing is to publish it under Grails Plugins Repository. This will make your plugin visible to the list-plugins command:

grails list-plugins

Which lists all plug-ins in the Grails Plug-in repository and also the plugin-info command:

grails plugin-info [plugin-name]

Which outputs more information based on the meta info entered into the plug-in descriptor.

If you have created a Grails plug-in and want it to be hosted in the central repository contact a member of the "G2One team"http://www.g2one.com and they can grant you access.

When you have access to the Grails Plug-in repository to release your plugin you simply have to execute the release-plugin command:

grails release-plugin

This will automatically commit changes to SVN, do some tagging and make your changes available via the list-plugins command.

12.2 Understanding a Plug-ins Structure

As as mentioned previously, a plugin is merely a regular Grails application with a contained plug-in descriptors. However when installed, the structure of a plugin differs slightly. For example, take a look at this plugin directory structure:

+ grails-app
     + controllers
     + domain
     + taglib
     etc.
 + lib
 + src
     + java
     + groovy
 + web-app
     + js
     + css

Essentially when a plugin is installed into a project, the contents of the grails-app directory will go into a directory such as plugins/example-1.0/grails-app. They will not be copied into the main source tree. A plugin never interferes with a project's primary source tree.

However, static resources such as those inside the web-app directory will be copied into the project's web-app directory under a special plugins directory. For example web-app/plugins/example-1.0/js.

It is therefore the responsibility of the plugin to make sure that it references static resources from the correct place. For example if you were referencing a JavaScript source from a GSP you could use:

<g:createLinkTo dir="/plugins/example/js" file="mycode.js" />

However this may cause a problem during development as the relative link when installed differs from when you're running the plugin standalone.

To make this easier there is a special pluginContextPath variable available that changes whether you're executing the plugin standalone or whether you've installed it into an application:

<g:createLinkTo dir="${pluginContextPath}/js" file="mycode.js" />

At runtime the pluginContextPath will either evaluate to an empty string or /plugins/example depending on whether the plugin is running standalone or has been installed in an application

Java & Groovy code that the plugin provides within the lib and src/java and src/groovy directories will be compiled into the main project's web-app/WEB-INF/classes directory so that they are made available at runtime.

12.3 Providing Basic Artefacts

Adding a new Script

A plugin can add a new script simply by providing the relevant Gant script within the scripts directory of the plugin:

+ MyPlugin.groovy
   + scripts     <-- additional scripts here
   + grails-app
        + controllers
        + services
        + etc.
    + lib

Adding a new Controller, Tag Library or Service

A plugin can add a new controller, tag libraries, service or whatever by simply creating the relevant file within the grails-app tree. Note that when the plugin is installed it will be loaded from where it is installed and not copied into the main application tree.

+ ExamplePlugin.groovy
   + scripts
   + grails-app
        + controllers  <-- additional controllers here
        + services <-- additional services here
        + etc.  <-- additional XXX here
    + lib

Providing Views, Templates and View resolution

When a plug-in provides a controller it may also provide default views to be rendered. This is an excellent way to modularize your application through plug-ins. The way it works is that Grails' view resolution mechanism will first look the view in the application it is installed into and if that fails will attempt to look for the view within the plug-in.

For example given a AmazonGrailsPlugin plug-in provided controller called BookController if the action being executed is list, Grails will first look for a view called grails-app/views/book/list.gsp then if that fails will look for the same view relative to the plug-in.

Note however that if the view uses templates that are also provided by the plug-in then the following syntax is necessary:

<g:render template="fooTemplate" contextPath="${pluginContextPath}"/>

Note the usage of the pluginContextPath variable as the value of the contextPath attribute. If this is not specified then Grails will look for the template relative to the application.

Excluded Artefacts

Note that by default, when packaging a plug-in, Grails will excludes the following files from the packaged plug-in:

If your plug-in does require files under the web-app/WEB-INF directory it is recommended that you modify the plug-in's scripts/_Install.groovy Gant script to install these artefacts into the target project's directory tree.

In addition, the default UrlMappings.groovy file is excluded to avoid naming conflicts, however you are free to add a UrlMappings definition under a different name which will be included. For example a file called grails-app/conf/BlogUrlMappings.groovy is fine.

12.4 Evaluating Conventions

Before moving onto looking at providing runtime configuration based on conventions you first need to understand how to evaluated those conventions from a plug-in. Essentially every plugin has an implicit application variable which is an instance of the GrailsApplication interface.

The GrailsApplication interface provides methods to evaluate the conventions within the project and internally stores references to all classes within a GrailsApplication using the GrailsClass interface.

A GrailsClass represents a physical Grails resources such as a controller or a tag library. For example to get all GrailsClass instances you can do:

application.allClasses.each { println it.name }

There are a few "magic" properties that the GrailsApplication instance possesses that allow you to narrow the type of artefact you are interested in. For example if you only want to controllers you can do:

application.controllerClasses.each { println it.name }

The dynamic method conventions are as follows:

The GrailsClass interface itself provides a number of useful methods that allow you to further evaluate and work with the conventions. These include:

For a full reference refer to the javadoc API.

12.5 Hooking into Build Events

Post-Install Configuration and Participating in Upgrades

Grails plug-ins can do post-install configuration and participate in application upgrade process (the upgrade command). This is achieved via two specially named scripts under scripts directory of the plugin - _Install.groovy and _Upgrade.groovy.

_Install.groovy is executed after the plugin has been installed and _Upgrade.groovy is executed each time the user upgrades his application with upgrade command.

These scripts are normal Gant scripts so you can use the full power of Gant. An addition to the standard Gant variables is the pluginBasedir variable which points at the plugin installation basedir.

As an example the below _Install.groovy script will create a new directory type under the grails-app directory and install a configuration template:

Ant.mkdir(dir:"${basedir}/grails-app/jobs")
Ant.copy(file:"${pluginBasedir}/src/samples/SamplePluginConfiguration.groovy",
         todir:"${basedir}/grails-app/conf")

// To access Grails home you can use following code: // Ant.property(environment:"env") // grailsHome = Ant.antProject.properties."env.GRAILS_HOME"

Scripting events

It is also possible to hook into command line scripting events through plug-ins. These are events triggered during execution of Grails target and plugin scripts.

For example, you can hook into status update output (i.e. "Tests passed", "Server running") and the creation of files or artefacts.

A plug-in merely has to provide a Events.groovy script to listen to the required events. Refer the documentation on Hooking into Events for further information.

12.6 Hooking into Runtime Configuration

Grails provides a number of hooks to leverage the different parts of the system and perform runtime configuration by convention.

Hooking into the Grails Spring configuration

First, you can hook in Grails runtime configuration by providing a property called doWithSpring which is assigned a block of code. For example the following snippet is from one of the core Grails plugins that provides i18n support:

import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;

class I18nGrailsPlugin {

def version = 0.1

def doWithSpring = { messageSource(ReloadableResourceBundleMessageSource) { basename = "WEB-INF/grails-app/i18n/messages" } localeChangeInterceptor(LocaleChangeInterceptor) { paramName = "lang" } localeResolver(CookieLocaleResolver) } }

This plugin sets up the Grails messageSource bean and a couple of other beans to manage Locale resolution and switching. It using the Spring Bean Builder syntax to do so.

Participating in web.xml Generation

Grails generates the WEB-INF/web.xml file at load time, and although plugins cannot change this file directly, they can participate in the generation of the file. Essentially a plugin can provide a doWithWebDescriptor property that is assigned a block of code that gets passed the web.xml as a XmlSlurper GPathResult.

Consider the below example from the ControllersPlugin:

def doWithWebDescriptor = { webXml ->
	def mappingElement = webXml.'servlet-mapping'
	mappingElement + {
		'servlet-mapping' {
			'servlet-name'("grails")
			'url-pattern'("*.dispatch")
		}
	}
}

Here the plugin goes through gets a reference to the last <servlet-mapping> element and appends Grails' servlet to the end of it using XmlSlurper's ability to programmatically modify XML using closures and blocks.

Doing Post Initialisation Configuration

Sometimes it is useful to be able do some runtime configuration after the Spring ApplicationContext has been built. In this case you can define a doWithApplicationContext closure property.

class SimplePlugin {
     def name="simple"
     def version = 1.1

def doWithApplicationContext = { appCtx -> SessionFactory sf = appCtx.getBean("sessionFactory") // do something here with session factory } }

12.7 Adding Dynamic Methods at Runtime

The Basics

Grails plugins allow you to register dynamic methods with any Grails managed or other class at runtime. New methods can only be added within a doWithDynamicMethods closure of a plugin.

For Grails managed classes like controllers, tag libraries and so forth you can add methods, constructors etc. using the ExpandoMetaClass mechanism by accessing each controller's MetaClass:

class ExamplePlugin {
  def doWithDynamicMethods = { applicationContext ->
        application.controllerClasses.each { controllerClass ->
             controllerClass.metaClass.myNewMethod = {-> println "hello world" }
        }
  }
}

In this case we use the implicit application object to get a reference to all of the controller classes' MetaClass instances and then add a new method called myNewMethod to each controller. Alternatively, if you know before hand the class you wish the add a method to you can simple reference that classes metaClass property:

class ExamplePlugin {

def doWithDynamicMethods = { applicationContext -> String.metaClass.swapCase = {-> def sb = new StringBuffer() delegate.each { sb << (Character.isUpperCase(it as char) ? Character.toLowerCase(it as char) : Character.toUpperCase(it as char)) } sb.toString() }

assert "UpAndDown" == "uPaNDdOWN".swapCase() } }

In this example we add a new method swapCase to java.lang.String directly by accessing its metaClass.

Interacting with the ApplicationContext

The doWithDynamicMethods closure gets passed the Spring ApplicationContext instance. This is useful as it allows you to interact with objects within it. For example if you where implementing a method to interact with Hibernate you could use the SessionFactory instance in combination with a HibernateTemplate:

import org.springframework.orm.hibernate3.HibernateTemplate

class ExampleHibernatePlugin {

def doWithDynamicMethods = { applicationContext ->

application.domainClasses.each { domainClass ->

domainClass.metaClass.static.load = { Long id-> def sf = applicationContext.sessionFactory def template = new HibernateTemplate(sf) template.load(delegate, id) } } } }

Also because of the autowiring and dependency injection capability of the Spring container you can implement more powerful dynamic constructors that use the application context to wire dependencies into your object at runtime:

class MyConstructorPlugin {

def doWithDynamicMethods = { applicationContext -> application.domainClasses.each { domainClass -> domainClass.metaClass.constructor = {-> return applicationContext.getBean(domainClass.name) } }

} }

Here we actually replace the default constructor with one that looks up prototyped Spring beans instead!

12.8 Participating in Auto Reload Events

Monitoring Resources for Changes

Often it is valuable to monitor resources for changes and then reload those changes when they occur. This is how Grails implements advanced reloading of application state at runtime. For example, consider the below simplified snippet from the ServicesPlugin that Grails comes with:

class ServicesGrailsPlugin {
    …
    def watchedResources = "file:./grails-app/services/*Service.groovy"

… def onChange = { event -> if(event.source) { def serviceClass = application.addServiceClass(event.source) def serviceName = "${serviceClass.propertyName}" def beans = beans { "$serviceName"(serviceClass.getClazz()) { bean -> bean.autowire = true } } if(event.ctx) { event.ctx.registerBeanDefinition(serviceName, beans.getBeanDefinition(serviceName)) } } } }

Firstly it defines a set of watchedResources as either a String or a List of strings that contain either the references or patterns of the resources to watch. If the watched resources is a Groovy file, when it is changed it will automatically be reloaded and passed into the onChange closure inside the event object.

The event object defines a number of useful properties:

From these objects you can evaluate the conventions and then apply the appropriate changes to the ApplicationContext and so forth based on the conventions, etc. In the "Services" example above, a new services bean is re-registered with the ApplicationContext when one of the service classes changes.

Influencing Other Plugins

As well as being able to react to changes that occur when a plugin changes, sometimes one plugin needs to "influence" another plugin.

Take for example the Services & Controllers plugins. When a service is reloaded, unless you reload the controllers too, problems will occur when you try to auto-wire the reloaded service into an older controller Class.

To get round this, you can specify which plugins another plugin "influences". What this means is that when one plugin detects a change, it will reload itself and then reload all influenced plugins. See this snippet from the ServicesGrailsPlugin:

def influences = ['controllers']

Observing other plugins

If there is a particular plugin that you would like to observe for changes but not necessary watch the resources that it monitors you can use the "observe" property:

def observe = ["hibernate"]

In this case when a Hibernate domain class is changed you will also receive the event chained from the hibernate plugin. It is also possible for a plugin to observe all loaded plugins by using a wildcard:

def observe = ["*"]

The Logging plugin does exactly this so that it can add the log property back to any artefact that changes while the application is running.

12.9 Understanding Plug-in Load Order

Controlling Plug-in Dependencies

Plug-ins often depend on the presence of other plugins and can also adapt depending on the presence of others. To cover this, a plugin can define two properties. The first is called dependsOn. For example, take a look at this snippet from the Grails Hibernate plugin:

class HibernateGrailsPlugin {
	def version = 1.0
	def dependsOn = [dataSource:1.0,
	                 domainClass:1.0,
	                 i18n:1.0,
	                 core: 1.0]

}

As the above example demonstrates the Hibernate plugin is dependent on the presence of 4 plugins: The dataSource plugin, The domainClass plugin, the i18n plugin and the core plugin.

Essentially the dependencies will be loaded first and then the Hibernate plugin. If all dependencies do not load, then the plugin will not load.

The dependsOn property also supports a mini expression language for specifying version ranges. A few examples of the syntax can be seen below:

def dependsOn = [foo:"* > 1.0"]
def dependsOn = [foo:"1.0 > 1.1"]
def dependsOn = [foo:"1.0 > *"]

When the wildcard * character is used it denotes "any" version. The expression syntax also excludes any suffixes such as -BETA, -ALPHA etc. so for example the expression "1.0 > 1.1" would match any of the following versions:

Controlling Load Order

Using dependsOn establishes a "hard" dependency in that if the dependency is not resolved, the plugin will give up and won't load. It is possible though to have a "weaker" dependency using the loadAfter property:

def loadAfter = ['controllers']

Here the plugin will be loaded after the controllers plugin if it exists, otherwise it will just be loaded. The plugin can then adapt to the presence of the other plugin, for example the Hibernate plugin has this code in the doWithSpring closure:

if(manager?.hasGrailsPlugin("controllers")) {
	openSessionInViewInterceptor(OpenSessionInViewInterceptor) {
        	flushMode = HibernateAccessor.FLUSH_MANUAL
	        sessionFactory = sessionFactory
	}
        grailsUrlHandlerMapping.interceptors << openSessionInViewInterceptor
  }

Here the Hibernate plugin will only register an OpenSessionInViewInterceptor if the controllers plugin has been loaded. The manager variable is an instance of the GrailsPluginManager interface and it provides methods to interact with other plugins and the GrailsPluginManager itself from any plugin.